<template>
  <view>
    <view class="content" @touchstart="hideDrawer">
      <scroll-view class="msg-list" scroll-y="true" :scroll-with-animation="scrollAnimation" :scroll-top="scrollTop"
                   :scroll-into-view="scrollToView" @scrolltoupper="loadHistory" upper-threshold="50">
        <view class="row" v-for="(row,index) in msgList" :key="index" :id="'msg'+row.id">
          <!-- 系统消息 -->
          <block v-if="row.type == 1">
            <view class="system">
              <!-- 文字消息 -->
              <view class="text">
                {{ row.content }}
              </view>
            </view>
          </block>
          <!-- 用户消息 -->
          <block v-if="row.type > 1">
            <!-- 自己发出的消息 -->
            <view class="my" v-if="row.uid == myUid">
              <!-- 左-消息 -->
              <view class="left">
                <!-- 文字消息 -->
                <view v-if="row.type == 2" class="bubble">
                  <rich-text :nodes="row.content"></rich-text>
                </view>
                <!-- 语音消息 -->
                <view v-if="row.type == 3" class="bubble voice" @tap="playVoice(row)" :class="playMsgid == row.id?'play':''">
                  <view class="length">{{row.length}}</view>
                  <view class="icon my-voice"></view>
                </view>
                <!-- 图片消息 -->
                <view v-if="row.type == 4" class="bubble img" @tap="showPic(row)">
                  <image :src="row.content" :style="{'width': row.w+'px','height': row.h+'px'}"></image>
                </view>

              </view>
              <!-- 右-头像 -->
              <view class="right">
                <avatar-group :type="row.avatarType" :avatar="row.avatar" :text="row.name"></avatar-group>
              </view>
            </view>
            <!-- 别人发出的消息 -->
            <view class="other" v-if="row.uid != myUid">
              <!-- 左-头像 -->
              <view class="left">
                <avatar-group :type="row.avatarType" :avatar="row.avatar" :text="row.name"></avatar-group>
              </view>
              <!-- 右-用户名称-时间-消息 -->
              <view class="right">
                <view class="username">
                  <view class="name">{{ row.name }}</view>
                  <view class="time">{{ row.gmtCreate | dateTimeFormat }}</view>
                </view>
                <!-- 文字消息 -->
                <view v-if="row.type == 2" class="bubble">
                  <rich-text :nodes="row.content"></rich-text>
                </view>
                <!-- 语音消息 -->
                <view v-if="row.type == 3" class="bubble voice" @tap="playVoice(row)" :class="playMsgid == row.id?'play':''">
                  <view class="icon other-voice"></view>
                  <view class="length">{{ row.length }}</view>
                </view>
                <!-- 图片消息 -->
                <view v-if="row.type == 4" class="bubble img" @tap="showPic(row)">
                  <image :src="row.content" :style="{'width': row.w+'px','height': row.h+'px'}"></image>
                </view>
              </view>
            </view>
          </block>
        </view>
      </scroll-view>
    </view>
    <!-- 抽屉栏 -->
    <view class="popup-layer" :class="popupLayerClass" @touchmove.stop.prevent="discard">
      <!-- 表情 -->
      <swiper class="emoji-swiper" :class="{hidden:hideEmoji}" indicator-dots="true" duration="150">
        <swiper-item v-for="(page,pid) in emojiList" :key="pid">
          <view v-for="(em,eid) in page" :key="eid" @tap="addEmoji(em)">
            <image mode="widthFix" :src="'/static/emoji/' + em.url"></image>
          </view>
        </swiper-item>
      </swiper>
      <!-- 更多功能 相册-拍照 -->
      <view class="more-layer" :class="{hidden:hideMore}">
        <view class="list">
          <view class="box">
            <u-upload @on-choose-complete="onChooseComplete" @on-success="onSuccess" :header="header" :max-count="50" :show-upload-list="false" :custom-btn="true" :source-type="chooseImage"  :action="action" :file-list="fileList" >
              <view slot="addBtn" class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
                <view class="icon tupian2"></view>
              </view>
            </u-upload>
          </view>
          <view class="box" @tap="camera">
            <u-upload @on-choose-complete="onChooseComplete" @on-success="onSuccess" :header="header" :max-count="1" :show-upload-list="false" :custom-btn="true"  :source-type="camera" :action="action" :file-list="fileList" >
              <view slot="addBtn" class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
                <view class="icon paizhao"></view>
              </view>
            </u-upload>
          </view>

        </view>
      </view>
    </view>
    <!-- 底部输入栏 -->
    <view class="input-box" :style="{bottom: b + 'px'}" :class="popupLayerClass" @touchmove.stop.prevent="discard">
      <!-- H5下不能录音，输入栏布局改动一下 -->
      <!-- #ifndef H5 -->
      <view class="voice">
        <view class="icon" :class="isVoice?'jianpan':'yuyin'" @tap="switchVoice"></view>
      </view>
      <!-- #endif -->
      <!-- #ifdef H5 -->
      <view class="more" @tap="showMore">
        <view class="icon add"></view>
      </view>
      <!-- #endif -->
      <view class="textbox">
        <view class="voice-mode" :class="[isVoice?'':'hidden',recording?'recording':'']" @touchstart="voiceBegin"
              @touchmove.stop.prevent="voiceIng" @touchend="voiceEnd" @touchcancel="voiceCancel">{{voiceTis}}</view>
        <view class="text-mode" :class="isVoice?'hidden':''">
          <view class="box">
            <textarea adjust-position="false" fixed="true" auto-height="true" v-model="textMsg" @focus="textareaFocus" @blur="textareaBlur"/>
          </view>
          <view class="em" @tap="chooseEmoji">
            <view class="icon biaoqing"></view>
          </view>
        </view>
      </view>
      <!-- #ifndef H5 -->
      <view class="more" @tap="showMore">
        <view class="icon add"></view>
      </view>
      <!-- #endif -->
      <view class="send" :class="isVoice?'hidden':''" @tap="sendText">
        <view class="btn">发送</view>
      </view>
    </view>
    <!-- 录音UI效果 -->
    <view class="record" :class="recording?'':'hidden'">
      <view class="ing" :class="willStop?'hidden':''"><view class="icon luyin2" ></view></view>
      <view class="cancel" :class="willStop?'':'hidden'"><view class="icon chehui" ></view></view>
      <view class="tis" :class="willStop?'change':''">{{recordTis}}</view>
    </view>
  </view>
</template>
<script>
import {getToken} from '@/common/auth.js';
import {websocketObj} from '@/utils/websocket.js';
import {DataContent} from '../../utils/chatObject.js';
import {networkStatus} from "../../utils/chat_utils";
import {timestampFormat} from "@/utils/date.js";
import avatarGroup from "@/components/avatar_group/avatar_group.vue"
import parseHtml from "../../utils/parse_html";
import {getEmojiList, getUrl} from "../../utils/constants";
import {GroupChatMsg} from "../../utils/chatObject";

export default {
  components: {
    avatarGroup
  },
  data() {
    return {
      b: 0,
      //文字消息
      textMsg:'',
      //消息列表
      isHistoryLoading:false,
      scrollAnimation:false,
      scrollTop:0,
      scrollToView:'',
      msgList:[],
      msgImgList:[],
      myUid:'',
      groupId: '',
      avatar: '',
      name: '',
      titleName: '',
      pageNum: 1,
      total: 0,
      //图片上传地址
      action: getUrl() + "/api/upload",
      fileList: [],
      header: {
        "token": getToken()
      },
      // 从相册中选
      chooseImage: ['album'],
      // 拍照
      camera: ['camera'],
      //录音相关参数
      // #ifndef H5
      //H5不能录音
      RECORDER:uni.getRecorderManager(),
      // #endif
      isVoice:false,
      voiceTis:'按住 说话',
      recordTis:"手指上滑 取消发送",
      recording:false,
      willStop:false,
      initPoint:{identifier:0,Y:0},
      recordTimer:null,
      recordLength: 0,

      //播放语音相关参数
      AUDIO:uni.createInnerAudioContext(),
      playMsgid:null,
      VoiceTimer:null,
      // 抽屉参数
      popupLayerClass:'',
      // more参数
      hideMore:true,
      //表情定义
      hideEmoji:true,
      emojiList: getEmojiList()
    };
  },
  async onLoad(option) {
    networkStatus()
    uni.$emit("setVuexUserInfo",{user:null,isLoginPage: false});
    this.myUid = this.$store.getters.id;
    this.avatar = this.$store.getters.avatar;
    this.name = this.$store.getters.name;
    this.groupId = option.id;
    this.titleName = option.name;
    await this.getMsgList();
    // 滚动到底部
    this.$nextTick(() => {
      //进入页面滚动到底部
      this.scrollTop = 9999;
      this.$nextTick(() => {
        this.scrollAnimation = true;
      });
    });
    //语音自然播放结束
    this.AUDIO.onEnded((res)=>{
      this.playMsgid=null;
    });
    // #ifndef H5
    //录音开始事件
    this.RECORDER.onStart((e)=>{
      this.recordBegin(e);
    })
    //录音结束事件
    this.RECORDER.onStop((e)=>{
      this.recordEnd(e);
    })
    // #endif

    uni.$on("receiveGroupMsg",this.receiveGroupMsg);
  },
  onUnload(){
    //移除监听
    uni.$off("receiveGroupMsg");
  },
  onShow(){
    this.scrollTop = 9999999;
  },
  onReady(){
    uni.setNavigationBarTitle({
      title: this.titleName
    });
  },
  methods:{
    // 接受消息(筛选处理)
    screenMsg(msg){
      //从长连接处转发给这个方法，进行筛选处理
      switch (msg.type){
        case 1:
          this.addSystemTextMsg(msg);
          break;
        case 2:
          this.addTextMsg(msg);
          break;
        case 3:
          this.addVoiceMsg(msg);
          break;
        case 4:
          this.addImgMsg(msg);
          break;
      }
      //非自己的消息震动
      if(msg.uid !== this.myUid){
        uni.vibrateLong({
          success: (res) => {

          }
        });
      }
      this.$nextTick(() => {
        // 滚动到底
        this.scrollToView = 'msg' + msg.id
      });
    },

    //触发滑动到顶部(加载历史信息记录)
    async loadHistory(e){
      if(this.isHistoryLoading){
        return ;
      }
      //参数作为进入请求标识，防止重复请求
      this.isHistoryLoading = true;
      //关闭滑动动画
      this.scrollAnimation = false;
      //记住第一个信息ID
      let viewId = this.msgList == null || this.msgList.length === 0 ? 0 : this.msgList[0].id;
      if(this.pageNum * 20 <= this.total) {
        this.pageNum = ++ this.pageNum;
        await this.getMsgList();
        //这段代码很重要，不然每次加载历史数据都会跳到顶部
        if(viewId !== 0){
          this.$nextTick( () => {
            //跳转上次的第一行信息位置
            this.scrollToView = 'msg' + viewId;
            this.$nextTick( () => {
              //恢复滚动动画
              this.scrollAnimation = true;
            });
          });
        }
      }
      this.isHistoryLoading = false;
    },
    // 加载初始页面消息
    async getMsgList(){
      let id = this.msgList == null || this.msgList.length === 0 ? 0 : this.msgList[0].id;
      let groupId = '';
      // 判断是否是班级
      if (this.groupId.startsWith('clazz:')){
        groupId = this.groupId.substring('clazz:'.length);
      }else {
        groupId = this.groupId;
      }
      this.$u.api.queryGroupMsg(this.pageNum, {groupId:groupId,id:id}).then(res => {
        let list = res.data;
        this.total = res.total;
        this.msgHandler(list);
      })
    },
    msgHandler(list){
      // 获取消息中的图片,并处理显示尺寸
      for(let i=0;i<list.length;i++){
        if(list[i].type === 4){
          let w = list[i].extend.split(":")[0];
          let h = list[i].extend.split(":")[1];
          list[i].w = w;
          list[i].h = h;
          list[i] = this.setPicSize(list[i]);
          this.msgImgList.unshift(list[i].content);
        }else if(list[i].type === 3){
          list[i].length = list[i].extend;
        }else if (list[i].type === 2){
          list[i].content = JSON.parse(list[i].content);
        }
      }
      this.msgList = list;
    },
    //处理图片尺寸，如果不处理宽高，新进入页面加载图片时候会闪
    setPicSize(imgMsg){
      // 让图片最长边等于设置的最大长度，短边等比例缩小，图片控件真实改变，区别于aspectFit方式。
      //350是定义消息图片最大宽度
      let maxW = uni.upx2px(350);
      //350是定义消息图片最大高度
      let maxH = uni.upx2px(350);
      if(imgMsg.w>maxW||imgMsg.h>maxH){
        let scale = imgMsg.w/imgMsg.h;
        imgMsg.w = scale>1?maxW:maxH*scale;
        imgMsg.h = scale>1?maxW/scale:maxH;
      }
      return imgMsg;
    },


    //更多功能(点击+弹出)
    showMore(){
      this.isVoice = false;
      this.hideEmoji = true;
      if(this.hideMore){
        this.hideMore = false;
        this.openDrawer();
      }else{
        this.hideDrawer();
      }
    },
    // 打开抽屉
    openDrawer(){
      this.popupLayerClass = 'showLayer';
    },
    // 隐藏抽屉
    hideDrawer(){
      this.popupLayerClass = '';
      setTimeout(()=>{
        this.hideMore = true;
        this.hideEmoji = true;
      },150);
    },
    // 图片上传成功是调用
    onSuccess(data, index, lists, name) {
      uni.getImageInfo({
        src: lists[index].url,
        success: (image)=>{
          let msg = {content:data.data,w:image.width,h:image.height};
          this.sendMsg(msg,4);
        },
        fail: (error) => {
          console.log(error)
        }
      });
    },
    // 选择图片完成是调用
    onChooseComplete(lists, name){
      this.hideDrawer();
    },
    // 选择表情
    chooseEmoji(){
      this.hideMore = true;
      if(this.hideEmoji){
        this.hideEmoji = false;
        this.openDrawer();
      }else{
        this.hideDrawer();
      }
    },
    //添加表情
    addEmoji(em){
      this.textMsg += em.alt;
    },

    //获取焦点，如果不是选表情ing,则关闭抽屉
    textareaFocus(){
      this.b = 13;
      if(this.popupLayerClass === 'showLayer' && this.hideMore === false){
        this.hideDrawer();
      }
    },
    textareaBlur(){
      this.b = 0;
    },
    // 发送文字消息
    sendText(){
      this.hideDrawer();//隐藏抽屉
      if(!this.textMsg){
        return;
      }
      let content = parseHtml(this.replaceEmoji(this.textMsg));
      this.sendMsg({content: content}, 2);
      this.textMsg = '';//清空输入框
    },
    //替换表情符号为图片
    replaceEmoji(str){
      let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g,(item, index)=>{
        console.log("item: " + item);
        for(let i=0;i<this.emojiList.length;i++){
          let row = this.emojiList[i];
          for(let j=0;j<row.length;j++){
            let EM = row[j];
            if(EM.alt === item){
              //在线表情路径，图文混排必须使用网络路径，请上传一份表情到你的服务器后再替换此路径
              //比如你上传服务器后，你的100.gif路径为https://www.xxx.com/emoji/100.gif 则替换onlinePath填写为https://www.xxx.com/emoji/
              let onlinePath = 'https://hufei.org.cn/'
              return '<img src="' + onlinePath + EM.url + '" alt="' + item + '">';
            }
          }
        }
      });
      return '<div style="display: flex;flex-wrap: wrap;align-items: center;word-wrap:break-word;">'+replacedStr+'</div>';
    },
    // 接收消息
    receiveGroupMsg({msg,groupId}){
      if (this.groupId === groupId || this.groupId === 'clazz:' + groupId) {
        this.screenMsg(msg);
      }
    },
    // 发送消息
    sendMsg(content,type){
      let nowDate = new Date();
      let lastId = this.msgList == null || this.msgList.length === 0 ? 0 : this.msgList[this.msgList.length - 1].id;
      lastId++;
      let avatarType = 1;
      if(this.$u.test.isEmpty(this.avatar)){
        avatarType = 3;
        if(this.$u.test.isEmpty(this.name)){
          this.name = "课";
        }
      }
      let obj = {id:lastId,uid: this.myUid,avatarType: avatarType,avatar:this.avatar,name:this.name,type:type,gmtCreate:nowDate}
      let msg = Object.assign(obj,content);
      let extendObj = {
        avatarType: avatarType,
        avatar: obj.avatar,
        name: obj.name
      }
      let tem = type === 2?JSON.stringify(content.content):content.content;
      let dataContent = new DataContent(3,new GroupChatMsg(this.myUid,this.groupId,type,tem,null),JSON.stringify(extendObj));
      let message = '';
      if(type === 3){
        dataContent.chatMsg.extend = content.length;
      }else if(type === 4){
        dataContent.chatMsg.extend = content.w + ":" + content.h;
      }else {
        message = msg.content;
      }
      // 发送消息
      websocketObj.sendMsg(dataContent);
      this.screenMsg(msg);
    },

    // 添加文字消息到列表
    addTextMsg(msg){
      this.msgList.push(msg);
    },
    // 添加语音消息到列表
    addVoiceMsg(msg){
      this.msgList.push(msg);
    },
    // 添加图片消息到列表
    addImgMsg(msg){
      msg = this.setPicSize(msg);
      this.msgImgList.push(msg.content);
      this.msgList.push(msg);
    },
    // 添加系统文字消息到列表
    addSystemTextMsg(msg){
      this.msgList.push(msg);
    },
    // 预览图片
    showPic(msg){
      uni.previewImage({
        indicator:"none",
        current:msg.content,
        urls: this.msgImgList
      });
    },
    // 播放语音
    playVoice(msg){
      this.playMsgid=msg.id;
      this.AUDIO.src = msg.content;
      this.$nextTick(function() {
        this.AUDIO.play();
      });
    },
    // 录音开始
    voiceBegin(e){
      if(e.touches.length>1){
        return ;
      }
      this.initPoint.Y = e.touches[0].clientY;
      this.initPoint.identifier = e.touches[0].identifier;
      this.RECORDER.start({format:"mp3"});//录音开始,
    },
    //录音开始UI效果
    recordBegin(e){
      this.recording = true;
      this.voiceTis='松开 结束';
      this.recordLength = 0;
      this.recordTimer = setInterval(()=>{
        this.recordLength++;
      },1000)
    },
    // 录音被打断
    voiceCancel(){
      this.recording = false;
      this.voiceTis='按住 说话';
      this.recordTis = '手指上滑 取消发送'
      this.willStop = true;//不发送录音
      this.RECORDER.stop();//录音结束
    },
    // 录音中(判断是否触发上滑取消发送)
    voiceIng(e){
      if(!this.recording){
        return;
      }
      let touche = e.touches[0];
      //上滑一个导航栏的高度触发上滑取消发送
      if(this.initPoint.Y - touche.clientY>=uni.upx2px(100)){
        this.willStop = true;
        this.recordTis = '松开手指 取消发送'
      }else{
        this.willStop = false;
        this.recordTis = '手指上滑 取消发送'
      }
    },
    // 结束录音
    voiceEnd(e){
      if(!this.recording){
        return;
      }
      this.recording = false;
      this.voiceTis='按住 说话';
      this.recordTis = '手指上滑 取消发送'
      this.RECORDER.stop();//录音结束
    },
    //录音结束(回调文件)
    recordEnd(e){
      clearInterval(this.recordTimer);
      if(!this.willStop){
        console.log("e: " + JSON.stringify(e));
        uni.uploadFile({
          url: this.action,
          name: "file",
          filePath:e.tempFilePath,
          header:this.header,
          success: res => {
            // 判断是否json字符串，将其转为json格式
            let data = this.$u.test.jsonString(res.data) ? JSON.parse(res.data) : res.data;
            if (![200, 201, 204].includes(data.code)) {
              this.$u.toast("发送失败")
            } else {
              // 上传成功
              let msg = {
                length:0,
                content: data.data
              }
              let min = Math.trunc(this.recordLength / 60);
              let sec = this.recordLength % 60;
              min = min < 10 ? '0' + min : min;
              sec = sec < 10 ? '0' + sec : sec;
              msg.length = min + ':' + sec;
              this.sendMsg(msg,3);
            }
          },
          fail: err => {
            this.$u.toast("发送失败")
          }
        })
      }else{
        console.log('取消发送录音');
      }
      this.willStop = false;
    },
    // 切换语音/文字输入
    switchVoice(){
      this.hideDrawer();
      this.isVoice = !this.isVoice;
    },
    discard(){
      return;
    }
  },
  filters:{
    dateTimeFormat: timestampFormat
  }
}
</script>
<style lang="scss">
@import "../chat/css/chat";
</style>
